#include "pvm/pvmapi.h"

#include <FBase.h>
#include <FBaseSys.h>
#include <FBaseString.h>
#include <FIo.h>
#include <FSysSystemTime.h>
#include "lib/pvmsystem.h"

#include "lib/memory/pvmmemory.h"

using namespace Osp::Io;

static PVMCriticalSection mmgrCriticalInstance;
static Osp::Base::Runtime::Mutex mmgrCriticalMutex;

class PVMBADAThread : public Osp::Base::Runtime::Thread
{
protected:
	PVMThreadStartRoutine routine;
	void *param;
public:
	PVMBADAThread(PVMThreadStartRoutine routine, void *param)
	{
		this->routine = routine;
		this->param = param;
	}
	virtual Osp::Base::Object *  Run(void)
	{
		routine(param);
		return null;
	}
};

PVMCriticalSection* PVMGetCriticalForMmgr()
{
	mmgrCriticalInstance = NULL;

	if(mmgrCriticalMutex.Create()!= E_SUCCESS)
	{
		return false;
	}

	mmgrCriticalInstance = &mmgrCriticalMutex;

	return &mmgrCriticalInstance;
}

void PVMFreeCriticalForMmgr()
{
	if(mmgrCriticalInstance==NULL)
		return;

	mmgrCriticalInstance = NULL;
}

pvm_bool PVMInitCritical(PVMCriticalSection *critical)
{
	if(critical==null)
		return false;
	*critical = new Osp::Base::Runtime::Mutex();
	if(*critical==null)
	{
		return false;
	}

	if((*critical)->Create()!= E_SUCCESS)
	{
		delete *critical;
		*critical = null;
		return false;
	}

	return true;
}

void PVMDeinitCritical(PVMCriticalSection *critical)
{
	if(critical!=null)
	{
		delete *critical;
		*critical = null;
	}
}


pvm_bool PVMEnterCritical(PVMCriticalSection *critical)
{
	if(critical==null)
		return false;
	if(*critical==null)
		return false;

	if((*critical)->Acquire()!=E_SUCCESS)
		return false;

	return true;
}

void PVMLeaveCritical(PVMCriticalSection *critical)
{
	if(critical==null)
		return;
	if(*critical==null)
		return;

	(*critical)->Release();
}

pvm_bool PVMInitEvent(PVMEventHandle *event)
{
	if(event==null)
		return false;
	*event = new Osp::Base::Runtime::Monitor();
	if(*event==null)
		return false;

	if((*event)->Construct()!=E_SUCCESS)
	{
		delete *event;
		*event = null;
		return false;
	}

	return true;
}

void PVMDeinitEvent(PVMEventHandle *event)
{
	if(event==null || *event==null)
		return;

	delete *event;
	*event = null;
}

pvm_bool PVMWaitForEvent(PVMEventHandle *event)
{
	if(event==null || *event==null)
		return false;

	if((*event)->Enter()!=E_SUCCESS)
		return false;

	pvm_bool error = false;
	if((*event)->Wait()!=E_SUCCESS)
		error = true;

	(*event)->Exit();

	return error;
}

void PVMSignalEvent(PVMEventHandle *event)
{
	if(event==null || *event==null)
		return;

	if((*event)->Notify()!=E_SUCCESS)
		return;

	return;
}


void PVMInitThreadVar(PVMThreadHandle* outHandle)
{
	if(outHandle==null)
		return;
	*outHandle = null;
}

pvm_bool PVMIsThreadValid(PVMThreadHandle threadHandle)
{
	return threadHandle!=null;
}

pvm_bool PVMCreateThread(PVMThreadHandle* outHandle, PVMThreadStartRoutine routine, void *param)
{
	PVMBADAThread* thread = null;
	if(!outHandle)
		return false;
	thread = new PVMBADAThread(routine, param);
	if(!thread)
			return false;

	if(thread->Construct()!= E_SUCCESS)
	{
		delete thread;
		return false;
	}

	if(thread->Start()!= E_SUCCESS)
	{
		delete thread;
		return false;
	}

	*outHandle = thread;

	return true;
}

int PVMJoinThread(PVMThreadHandle threadHandle)
{
	if(threadHandle==null)
		return -1;

	if(threadHandle->Join()!=E_SUCCESS)
		return -2;

	return 0;
}

void PVMSleep(pvm_uint32 sleepTime)
{
	Osp::Base::Runtime::Thread::Sleep(sleepTime);
}

pvm_uint32 PVMGetMilliSecondsCount()
{
	long long ticks = 0;
	Osp::System::SystemTime::GetTicks(ticks);
	return (pvm_uint32)ticks;
}


PVMFileHandle PVMFileOpen(const pvm_char* fileName, pvm_int32 mode)
{
	Osp::Base::String modeStr;

	if(mode&PVM_READ && mode&PVM_WRITE && mode&PVM_OPEN_EXISTING)
	{
		modeStr.Append(L"r+b");
	}
	else if(mode&PVM_READ && mode&PVM_WRITE && mode&PVM_CREATE_ALWAYS)
	{
		modeStr.Append(L"w+b");
	}
	else if(mode&PVM_READ && mode&PVM_WRITE && mode&PVM_OPEN_ALWAYS)
	{
		modeStr.Append(L"a+b");
	}
	else if(mode&PVM_READ && mode&PVM_OPEN_EXISTING)
	{
		modeStr.Append(L"rb");
	}
	else if(mode&PVM_WRITE && mode&PVM_CREATE_ALWAYS)
	{
		modeStr.Append(L"wb");
	}
	else if(mode&PVM_WRITE && mode&PVM_OPEN_ALWAYS)
	{
		modeStr.Append(L"ab");
	}

	PVMFileHandle file = new Osp::Io::File();
	if(file->Construct((const wchar_t*)fileName, modeStr)!=E_SUCCESS)
	{
		delete file;
		return null;
	}
	return file;
}

void PVMFileClose(PVMFileHandle file)
{
	delete file;
}

pvm_bool PVMFileRead(PVMFileHandle file, void* buffer, pvm_size nBytes, pvm_size* readResult)
{
	if(readResult==NULL)
		return false;

	*readResult = file->Read(buffer, nBytes);
	if(GetLastResult()!=E_SUCCESS)
		return false;

	return true;
}

pvm_bool PVMFileWrite(PVMFileHandle file, void* buffer, pvm_size nBytes, pvm_size* writeResult)
{
	if(writeResult==NULL)
		return false;
	*writeResult = 0;
	if(file->Write(buffer, nBytes)!=E_SUCCESS)
		return false;
	*writeResult = nBytes;
	return true;
}

pvm_file_size PVMFileSize(PVMFileHandle file)
{
	if(file==null)
		return PVM_INVALID_FILE_SIZE;

	int curPos = file->Tell();
	if(curPos<0)
		return PVM_INVALID_FILE_SIZE;

	if(file->Seek(FILESEEKPOSITION_END, 0)!=E_SUCCESS)
		return PVM_INVALID_FILE_SIZE;

	int fileSize = file->Tell();
	if(file->Seek(FILESEEKPOSITION_BEGIN, curPos)!=E_SUCCESS)
		return PVM_INVALID_FILE_SIZE;

	if(fileSize<0)
		return PVM_INVALID_FILE_SIZE;

	return (pvm_file_size)fileSize;
}

pvm_bool PVMFileSeek(PVMFileHandle file, pvm_file_size pos, pvm_int32 origin)
{
	Osp::Io::FileSeekPosition moveMethod = FILESEEKPOSITION_BEGIN;

	 switch(origin)
	 {
	 case EPVM_SEEK_BEGIN:
		moveMethod = FILESEEKPOSITION_BEGIN;
		break;
	 case EPVM_SEEK_CURRENT:
		moveMethod = FILESEEKPOSITION_CURRENT;
		break;
	 case EPVM_SEEK_END:
		moveMethod = FILESEEKPOSITION_END;
		break;
	 };

	 if(file->Seek(moveMethod, pos)!=E_SUCCESS)
		 return false;

	 return true;
}

pvm_file_size PVMFileTell(PVMFileHandle file)
{
	if(file==null)
		return PVM_INVALID_FILE_SIZE;

	int fileSize = file->Tell();
	if(fileSize<0)
		return PVM_INVALID_FILE_SIZE;

	return fileSize;
}

pvm_bool PVMSystemInit()
{
	return true;
}

void PVMSystemDeinit()
{
}

void PVMSysLogOutString(const pvm_char* logString)
{
	__App_info(__PRETTY_FUNCTION__ , __LINE__, "%S", logString);
}
